// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Author: Markus Gutschke

#include <stdlib.h>

#include "check.h"
#include "utf8.h"

char *encodeUTF8(char *buf, unsigned codepoint) {
  check(codepoint);
  check(codepoint < 0x80u || codepoint > 0x9Fu);
  check(codepoint < 0xD800u || codepoint > 0xE000u);
  check(codepoint != 0xFFFEu);
  check(codepoint < 0x110000u);
  int len = 0;
  for (unsigned mask = ~0x7Fu; codepoint & mask; ++len, mask <<= 5) { }
  if (!len) {
    *buf++ = codepoint;
  } else {
    *buf++ = (unsigned char)(~((1u << (7-len)) - 1u) | (codepoint >> (6*len)));
    while (len--) {
      *buf++ = (unsigned char)(0x80u | ((codepoint >> (6*len)) & 0x3Fu));
    }
  }
  return buf;
}

const char *parseUTF8(const char *txt, unsigned *codepoint) {
  if (!*txt) {
    *codepoint = 0;
    return NULL;
  } else if (*(unsigned char *)txt < 0x80) {
    *codepoint = *(unsigned char *)txt++;
    return txt;
  } else {
    int len = 0;
    for (unsigned char ch = *(unsigned char *)txt; (ch<<=1) & 0x80u; ++len) { }
    check(len > 0);
    check(len < 6);
    unsigned cp = *(unsigned char *)txt++ & ((1u << (6-len))-1u);
    while (len--) {
      unsigned char ch = *(unsigned char *)txt++;
      check(ch);
      check((ch & 0xC0u) == 0x80);
      cp = (cp << 6) | (ch & 0x3Fu);
    }
    check(cp > 0x9Fu);
    check(cp < 0xD800u || cp > 0xE000u);
    check(cp != 0xFFFEu);
    check(cp < 0x110000u);
    *codepoint = cp;
    return txt;
  }
}
